package MuleBackend;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.Timer;

import MuleGUI.BoardGUI;


/**
 * The stage class controls the flow of 
 * the stages in the mule
 * @author Spyro
 *
 */
public class Stages {
	TimeClass tc;	
	Timer timer;
	int numTurns, currentStage, numAuction,numPlay,round;
	ArrayList<Player> playerList;
	static int playerListIndex = 0;
	static int timeAllotted;
	boolean hasRandomed;
	
	/**
	 * constructor for the stages
	 * knows various pieces of information
	 * needed to help with the stage changes
	 */
	public Stages(){
		playerList = null;
		numTurns = 0;
		currentStage = 1;
		numAuction = 0;
		numPlay = 0;
		round = 1;
		tc = new TimeClass();
		timer = new Timer(1000, tc);
		timer.setInitialDelay(0);
		hasRandomed=false;
	}
	
	/**
	 * Ends the current turn (used for pub gambling
	 * where the turns can end prematurely)
	 */
	public void endTimer() {
		timer.stop();
		tc.setCounter(100);
		GameDriver.game.getJProgressBar().setValue(0);
		++currentStage;
		GameDriver.game.getBoardGUI().hideRandomLabel();
		startStage(currentStage);
	}
	
	/**
	 * gets the current stage
	 * @return the current stage
	 */
	public int getCurrentStage() { return currentStage; }
	
	/**
	 * helper method to reset the player list index back to 0
	 */
	private void resetPlayerListIndex() { playerListIndex = 0; }
	
	/**
	 * helper method to increase the player list index
	 */
	private void incrementPlayerListIndex() { playerListIndex++; }
	
	/**
	 * method that sets the playerlist with the list of players in the game
	 */
	public void setPlayerList() { playerList = GameDriver.game.getPlayerList(); }
	
	/**
	 * getter method to get the number of turns
	 * @return the number of turns
	 */
	public int getNumTurns() { return numTurns; }
	
	/**
	 * getter method to get the time alloted to each player
	 * @return the time allotted to a player's turn
	 */
	public static int getTimeAllotted() { return timeAllotted; }
	
	/**
	 * starts the next stage
	 */
	public void incrementStage() { startStage(++currentStage); }
	
	
	/**
	 * private helper method to start the timer
	 * @param time the number of seconds for each stage
	 */
	private void startTimer(int time) {
		timer.setDelay(time*10);
		timer.start();
	}
	
	/**
	 * the order of stages
	 * @param the number of stage
	 */
	public void startStage(int i){
		tc.setTimeBar();
		timer.stop();
		GameDriver.game.updateInfoPanels();
//		GameDriver.game.statusGUI.update();
		currentStage=i;
		switch(i){
		case 1:
			GameDriver.game.setCurrentPlayer(null);
			resetPlayerListIndex();
			status();
			break;
		case 2:
			freeLand();
			break;
		case 3:
			GameDriver.game.setCurrentPlayer(null);
			landSale();
			resetPlayerListIndex();
			break;
		case 4:
			GameDriver.game.setCurrentPlayer(null);
			resetPlayerListIndex();
			landAuction();
			break;
		case 5:
			GameDriver.game.boardGUI.setAuctionWinner();
			GameDriver.game.boardGUI.setMule(false);
			GameDriver.game.boardGUI.removeTownPanel();
			GameDriver.game.boardGUI.stopPlayer();
			turns();
			break;
		case 6:
			GameDriver.game.boardGUI.setMule(false);
			GameDriver.game.boardGUI.removeTownPanel();
			GameDriver.game.boardGUI.stopPlayer();
			production();
			break;
		case 7:
			goodsAuction();
			break;
		default: 
			GameDriver.game.startStage();
			break;
		}
	}
	
	/**
	 * this stage will display the scores
	 * of each player
	 */
	public void status()
	{		
		Player.sortPlayers(GameDriver.game.playerList);
		numAuction = 0;
//		GameDriver.game.statusGUI.setVisible(true);
		GameDriver.game.getBoardGUI().addStatusPanel();
		startTimer(1);
	}
	
	/**
	 * this method will allow all the players
	 * to have a turn to choose a free land
	 */
	public void freeLand() {
		GameDriver.game.getBoardGUI().setTitle("Land Grant");
		GameDriver.game.setCurrentStage("freeLand");
		if (numPlay==0){
			numPlay++;
//			GameDriver.game.boardGUI.switchToLandListeners();
			
			Player actualPlayer = playerList.get(playerListIndex);	
//			GameDriver.game.statusGUI.setVisible(false);
			GameDriver.game.getBoardGUI().removeStatusPanel();
			
	    	JOptionPane.showMessageDialog(new JFrame(), actualPlayer.getName() + "'s turn!");
	    	currentStage--;
	    	startTimer(5);
			GameDriver.game.setCurrentPlayer(actualPlayer);
			actualPlayer.setMule(0);
			incrementPlayerListIndex();
		} else {
			if (++numPlay<GameDriver.game.numPlayers)
				currentStage--;
						
			Player actualPlayer = playerList.get(playerListIndex);
			GameDriver.game.setCurrentPlayer(actualPlayer);
			
	    	JOptionPane.showMessageDialog(new JFrame(), actualPlayer.getName() + "'s turn!");
			startTimer(5);
			actualPlayer.setMule(0);
			incrementPlayerListIndex();
		}
	}
	
	/**
	 * land sale 
	 * this stage choose random plane from the board
	 */
	public void landSale(){
		GameDriver.game.getBoardGUI().setTitle("Land Sale");
		GameDriver.game.setCurrentStage("Land Sale");
		Land landForSale = GameDriver.game.getBoardGUI().startLandSale();
		GameDriver.game.getBoard().setNextFreeLand(landForSale);
		numPlay=0;
		startTimer(5);
	}
	
	/**
	 * auction for land sale
	 * this stage allow player to bid for the land which is 
	 * currently on sale
	 */
	public void landAuction(){
		GameDriver.game.setCurrentStage("Land Auction");
//		GameDriver.game.landAuctionGUI.setVisible(true);
		GameDriver.game.getBoardGUI().addLandAuction();

		Land auctionLand = GameDriver.game.getBoard().getNextFreeLand();
		if(auctionLand==null)
			startStage(++currentStage);
		GameDriver.game.getBoardGUI().startLandAuction(auctionLand);
		startTimer(10);
//		GameDriver.game.landAuctionGUI.jProgressBar();
	}
		
	/**
	 * player turns
	 * this method allows all the players to have a turn
	 */
	public void turns(){
		GameDriver.game.setCurrentStage("turns");
		if (numTurns==0){
			numTurns++;
				//testing
			GameDriver.game.getBoardGUI().hideLandAuction();
			Player actualPlayer = playerList.get(playerListIndex);
			GameDriver.game.setCurrentPlayer(actualPlayer);
			incrementPlayerListIndex();
			
			//initialize all the necessary values and variables
//			GameDriver.game.townGUI.setPlayer(actualPlayer);		
			
	    	JOptionPane.showMessageDialog(new JFrame(), actualPlayer.getName() + "'s turn!");
	    	System.out.println("Start Of New Turn!");
	    	System.out.println(actualPlayer.getName() + "'s turn!");
			
	    	if(GameDriver.game.getDifficulty() == "Easy");
			timeAllotted = actualPlayer.getTurnTime(round);
			
			if(GameDriver.game.getDifficulty() == "Medium")
			timeAllotted = actualPlayer.getMediumTurnTime(round);
			
			if(GameDriver.game.getDifficulty() == "Difficult")
			timeAllotted = actualPlayer.getDifficultTurnTime(round);
			
			GameDriver.game.updateInfoPanels();
			
//			GameDriver.game.landAuctionGUI.setVisible(false);

			GameDriver.game.getBoardGUI().setTitle(actualPlayer.getName() +"'s TURN");	//temporary
			currentStage--;
			GameDriver.game.boardGUI.addTownPanel(405,245); //post-edit
			
			//perform turn proper
			this.randomEvent();
//			startTimer(timeAllotted);
			startTimer(50);
			
		}else if (numTurns++<GameDriver.game.numPlayers){
				//testing
			Player actualPlayer = playerList.get(playerListIndex);
			GameDriver.game.setCurrentPlayer(actualPlayer);
			incrementPlayerListIndex();
			
			currentStage--;
			
			if(GameDriver.game.getDifficulty() == "Easy");
			timeAllotted = actualPlayer.getTurnTime(round);
			
			if(GameDriver.game.getDifficulty() == "Medium")
			timeAllotted = actualPlayer.getMediumTurnTime(round);
			
			if(GameDriver.game.getDifficulty() == "Difficult")
			timeAllotted = actualPlayer.getDifficultTurnTime(round);
			
			GameDriver.game.updateInfoPanels();
//			GameDriver.game.townGUI.setPlayer(actualPlayer);
			
	    	JOptionPane.showMessageDialog(new JFrame(), actualPlayer.getName() + "'s turn!");
	    	System.out.println("Start Of New Turn!");
	    	System.out.println( actualPlayer.getName() + "'s turn!");
			
			GameDriver.game.boardGUI.removeTownPanel();
			
			GameDriver.game.boardGUI.addTownPanel(405,245);	//post edit
			
			GameDriver.game.getBoardGUI().setTitle(actualPlayer.getName() +"'s TURN");	//temporary
			this.randomEvent();
			startTimer(timeAllotted);
		}else{
			hasRandomed=false;
			endTimer();
		}
	}
	
	/**
	 * production stage
	 * this stage will show how much is produce
	 */
	public void production(){
		GameDriver.game.getBoardGUI().setTitle("Production");
		numTurns=0;
		GameDriver.game.boardGUI.removeTownPanel();
		GameDriver.game.boardGUI.showProduction();
		startTimer(5);
		ArrayList<Player> playerlist = GameDriver.game.getPlayerList();

		for(int i = 0; i< playerList.size(); i++)
		{
			if(GameDriver.game.getDifficulty() == "Easy")
				calculateProduction(playerList.get(i));
				
				else if(GameDriver.game.getDifficulty() == "Medium")
					calculateMediumProduction(playerList.get(i));
				
				else
					calculateDifficultProduction(playerList.get(i));
		}
		
		GameDriver.game.updateInfoPanels();
	}
	
	/**
	 * Auction for 4 types of goods
	 * this method will allow different types of goods to 
	 * have an auction phrase
	 */
	public void goodsAuction(){
		GameDriver.game.getBoardGUI().setTitle("Mule Game");
		if (numAuction==0){
			numAuction++;
			GameDriver.game.boardGUI.hideProduction();
			GameDriver.game.boardGUI.addGoodAuction("Food");
			currentStage--;
			startTimer(30);
		}else if (numAuction==1){
			numAuction++;
			GameDriver.game.boardGUI.hideGoodAuction();
			GameDriver.game.boardGUI.addGoodAuction("Energy");
			currentStage--;
			startTimer(30);
		}else if (numAuction==2){
			numAuction++;
			GameDriver.game.boardGUI.hideGoodAuction();
			GameDriver.game.boardGUI.addGoodAuction("Smithore");
			currentStage--;
			startTimer(30);
		}else if (numAuction==3){
			numAuction++;
			GameDriver.game.boardGUI.hideGoodAuction();
			GameDriver.game.boardGUI.addGoodAuction("Crystite");
			currentStage--;
			startTimer(30);
		}else{
			GameDriver.game.boardGUI.hideGoodAuction();
			GameDriver.game.startStage();
		}
	}
	
	/**
	 * when called, a random event has a 2/3 chance of happening for the player whose turn
	 * this method was called in.
	 * if no random events have happened, a random event is guaranteed to happen to the last player.
	 * 
	 * Bad events will not happen to the last place player.
	 * Good events will not happen to first place player.
	 */
	public void randomEvent(){
		Player player= GameDriver.game.getCurrentPlayer();
		BoardGUI b=GameDriver.game.boardGUI;
		
		double r=Math.random();
		if(r>0.66 && player.getPlayerID()<GameDriver.game.getNumPlayers()){
			r=Math.random();
			if(r>0.8){			
				b.showRandomLabel("The museum bought your antique personal computer for $"+ 
						8*round);
				player.addMoney(8*round);
			}else if(r>0.6){
				b.showRandomLabel("You just received a package from a GT alumni containing 3 food and 2 energy units");
				player.addFood(3);
				player.addEnergy(2);
			}else if(r>0.4){
				b.showRandomLabel("A wandering tech student repaid your hospitality by leaving two bars of ore");
				player.addSmithore(2);
			}else if(r>0.2){
				b.showRandomLabel("You found a dead moose rat and sold the hide for $"+ 2*GameDriver.game.getRoundNumber());
				player.addMoney(2*round);
			}else{
				b.showRandomLabel("Da-na-na-na! You broke into a villager's house and found 2 crystite in his pottery.");
				player.addCrystite(2);
			}
			hasRandomed=true;
		}else if(r<0.33 && player != GameDriver.game.getPlayerList().get(0)||
				(player.getPlayerID()==GameDriver.game.getNumPlayers() && !hasRandomed)){
			r=Math.random();
			if(r>0.75){
				int subtracted;
				if(player.getMoney()<4*round){
					subtracted=player.getMoney();
					player.deductMoney(player.getMoney());
				}else{
					subtracted=4*round;
					player.deductMoney(4*round);
				}
				b.showRandomLabel("flying cat-bugs ate the roof off your house. repairs cost $" +subtracted);
			}else if(r>0.5){
				b.showRandomLabel("Mischievious UGA students broke into your storage shed and stole half your food");
				player.deductFood(player.getFood()/2);
			}else if (r>0.25){
				int subtracted;
				if(player.getMoney()<6*round){
					subtracted=player.getMoney();
					player.deductMoney(player.getMoney());
				}else{
					subtracted=6*round;
					player.deductMoney(6*round);
				}
				b.showRandomLabel("Your space gypsy inlaws made a mess of the town. repairs cost $"+subtracted);
			}else{
				b.showRandomLabel("A hellish week of studying and all-nighters have drained a third of your energy reserves.");
				player.addEnergy((player.getEnergy()/3)*-1);
			}
			hasRandomed=true;
		}
		
	}
	//checks if the player's  If it is not, nothing happens. 
	//Otherwise, it subtracts the end of turn energy 
	//cost (3) and then 
	/**
	 * Takes in a player as an argument and creates a list of that player's owned lands. It then
	 * begins iterating through the list of that player's owned lands. For each land, if a 
	 * factory is present AND that player's energy is greater than 0, it adds the land's production
	 * capacity of the land's factory type to a production total for that type of good. Otherwise, 
	 * if there is no factory or the player's energy is 0, no goods are added. At the end of the 
	 * method, the player is granted the grand total of each type of good.
	 * 
	 * This method is to be called exclusively for EACH player at the beginning of the production 
	 * phase.
	 * 
	 * @author Phil
	 * 
	 * @param player The player to calculate production cost for.
	 */
	
	public static void calculateProduction(Player player)
	{
		int factory;

		int foodProduction = 0;
		int energyProduction = 0;
		int smithoreProduction = 0;
		int crystiteProduction = 0;
		
		ArrayList<Land> playerLands = player.getLands();


			for(int i = 0; i < playerLands.size(); i++)
			{
				factory = playerLands.get(i).getFactory();
				
				if(factory == 0)
					continue;
				
				else if(factory == 1 && player.getEnergy() > 0)
				{
					player.addEnergy(-1);
					foodProduction += playerLands.get(i).food;
				}
				
				else if(factory == 2 && player.getEnergy() > 0)
				{
					player.addEnergy(-1);
					energyProduction += playerLands.get(i).energy;
				}
				
				else if(factory == 3 && player.getEnergy() > 0)
				{
					player.addEnergy(-1);
					smithoreProduction += playerLands.get(i).smithore;
				}
				
				else if(factory == 4 && player.getEnergy() > 0)
				{
					player.addEnergy(-1);
					crystiteProduction += playerLands.get(i).crystite;
				}
			} 
			player.addFood(foodProduction);
			player.addEnergy(energyProduction);
			player.addSmithore(smithoreProduction);
			player.addCrystite(crystiteProduction);
			
			//Println showing production for the current player, for demonstration purposes:
			
			System.out.println("Total food/energy/smithore/crystite produced for player " + 
			player.getName() + "is: " + foodProduction +"/"+ energyProduction +"/"+ 
			smithoreProduction +"/"+ crystiteProduction);
			
	}
	
	/*
	Similar to calculateProduction, only the energy requirements are 2 per factory	
	*/
	public static void calculateMediumProduction(Player player)
	{
		int factory;

		int foodProduction = 0;
		int energyProduction = 0;
		int smithoreProduction = 0;
		int crystiteProduction = 0;
		
		ArrayList<Land> playerLands = player.getLands();


			for(int i = 0; i < playerLands.size(); i++)
			{
				factory = playerLands.get(i).getFactory();
				
				if(factory == 0)
					continue;
				
				else if(factory == 1 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					foodProduction += playerLands.get(i).food;
				}
				
				else if(factory == 2 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					energyProduction += playerLands.get(i).energy;
				}
				
				else if(factory == 3 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					smithoreProduction += playerLands.get(i).smithore;
				}
				
				else if(factory == 4 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					crystiteProduction += playerLands.get(i).crystite;
				}
			} 
			player.addFood(foodProduction);
			player.addEnergy(energyProduction);
			player.addSmithore(smithoreProduction);
			player.addCrystite(crystiteProduction);
			
			//Println showing production for the current player, for demonstration purposes:
			
			System.out.println("Total food/energy/smithore/crystite produced for player " + 
			player.getName() + "is: " + foodProduction +"/"+ energyProduction +"/"+ 
			smithoreProduction +"/"+ crystiteProduction);
			
	}
	
	/*
	Similar to calculateProduction, only the energy requirements are 2 per factory
	and the factories also produce half their normal output.	
	*/
	public static void calculateDifficultProduction(Player player)
	{
		int factory;

		int foodProduction = 0;
		int energyProduction = 0;
		int smithoreProduction = 0;
		int crystiteProduction = 0;
		
		ArrayList<Land> playerLands = player.getLands();


			for(int i = 0; i < playerLands.size(); i++)
			{
				factory = playerLands.get(i).getFactory();
				
				if(factory == 0)
					continue;
				
				else if(factory == 1 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					foodProduction += (playerLands.get(i).food)/2;
				}
				
				else if(factory == 2 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					energyProduction += (playerLands.get(i).energy)/2;
				}
				
				else if(factory == 3 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					smithoreProduction += (playerLands.get(i).smithore)/2;
				}
				
				else if(factory == 4 && player.getEnergy() > 0)
				{
					player.addEnergy(-2);
					crystiteProduction += (playerLands.get(i).crystite)/2;
				}
			} 
			player.addFood(foodProduction);
			player.addEnergy(energyProduction);
			player.addSmithore(smithoreProduction);
			player.addCrystite(crystiteProduction);
			
			//Println showing production for the current player, for demonstration purposes:
			
			System.out.println("Total food/energy/smithore/crystite produced for player " + 
			player.getName() + "is: " + foodProduction +"/"+ energyProduction +"/"+ 
			smithoreProduction +"/"+ crystiteProduction);
			
	}
	
	/**
	 * getter method to get the stage's timer
	 * @return
	 */
	public Timer getTimer() {
		return timer;
	}
	
	/**
	 * time class that cycles through to the next stages
	 * whenever the timer runs out
	 * @author Jackson
	 *
	 */
	public class TimeClass implements ActionListener {
		int counter;
		JProgressBar timeBar;

		/**
		 * constructor for the TimeClass
		 * @param count the length of time for the timer?
		 */
		public TimeClass() {
			counter = 100;
			timeBar = null;
		}

		/**
		 * setter method to set the time bar of the timer
		 */
		public void setTimeBar() {
			timeBar = GameDriver.game.getJProgressBar();
		}
		
		/**
		 * setter method to set the counter
		 * @param num
		 */
		public void setCounter(int num) {
			counter = num;
		}
		/**
		 * getter method to get the counter
		 * @return
		 */
		public int getCounter() {
			return counter;
		}
		
		
		/**
		 * action to perform when timer hits 0
		 * updates stage
		 *
		 * Changes phase for good auction to so that players can bid/sell
		 */
		public void actionPerformed(ActionEvent tc) {	
			if((currentStage == 6 || currentStage == 7) && counter == 85){
				//System.out.println("PHASE CHANGED");
				GameDriver.game.getBoardGUI().updateAuctionPhase();
			}
			timeBar.setValue(counter);
			timeBar.repaint();
			if (counter == 0) {
				counter = 100;
				startStage(++currentStage);
				GameDriver.game.getBoardGUI().hideRandomLabel();
			}
			counter--;
		}
		
	}	//end of time class
}
